﻿using System;
using System.Collections.Generic;
using System.IO;


namespace FuzzScript
{
    
    /// <summary>
    /// Control Engine Interface. Each Control Engine must implement a Control method.
    /// </summary>
    public interface IControlEngine
    {
        void Control(Dictionary<string, double> p_InternalState, Dictionary<string, double> p_ControlValues);
    }

    /// <summary>
    /// Fuzzy Control Engine.
    /// </summary>
    public class FuzzyEngine :IControlEngine
    {
        
        /// <summary>
        /// Actual fuzzy system
        /// </summary>
        FuzzySystem m_System = null; 
               
        /// <summary>
        /// Constructor. Generates a fuzzy system given a definition file as parameter
        /// </summary>
        /// <param name="p_sDefinition">Path to a fuzzy system definition file</param>
        public FuzzyEngine(string p_sDefinition)
        {
            Parser parser=null;
            try
            {
                Scanner scanner = new Scanner(p_sDefinition);
                parser = new Parser(scanner);
                parser.Parse();
                Console.Write(parser.errors.count + " errors detected\n");
                m_System = parser.m_System;

#if DEBUG
                Console.Write(m_System.ToString() + "\n");
#endif

            }
            catch (Exception e)
            {

                if (parser.errors.count > 0)
                    Console.Write(parser.errors.count + " errors detected\n");
                else
                    Console.Write(e.Message);

                throw new Exception("Error in parsing " + p_sDefinition+"\n");
            }

        }

        /// <summary>
        /// Control Method. This is executed in every iteration
        /// </summary>
        /// <param name="p_InternalState">System inputs</param>
        /// <param name="p_ControlValues">System outputs</param>
        public void Control( Dictionary<string, double> p_InternalState, Dictionary<string, double> p_ControlValues)
        {

            foreach (KeyValuePair<string, double> internalState in p_InternalState)
            {
                ((FuzzyVariable)SymbolTable.Instance.Get(internalState.Key)).Value = internalState.Value;
            }

            m_System.Execute();


            if (m_ControlValues == null)
            {
                m_ControlValues = new Dictionary<string, double>();

                foreach (string s in p_ControlValues.Keys)
                {
                    m_ControlValues.Add(s,
                        ((FuzzyVariable)SymbolTable.Instance.Get(s)).Value);
                }
            }
            else
            {
                foreach (KeyValuePair<string, double> controlValue in p_ControlValues)
                {
                    double d = ((FuzzyVariable)SymbolTable.Instance.Get(controlValue.Key)).GetOutputValue();
                    m_ControlValues[controlValue.Key] = d;
                }
            }

            foreach (KeyValuePair<string, double> controlValue in m_ControlValues)
            {
                p_ControlValues[controlValue.Key] = m_ControlValues[controlValue.Key];
            }
        }


        /// <summary>
        /// temporary container of output values
        /// </summary>
        Dictionary<string, double> m_ControlValues = null;

        /// <summary>
        /// Generates an optimized fuzzy system. 
        /// </summary>
        /// <returns>an</returns>
        public IControlEngine Generate()
        {
            string sSystemCode = m_System.Generate();

            // compile the new system and return reference
            return GenerateControlSystem(sSystemCode);
        }

        /// <summary>
        /// Generates an optimized fuzzy system. Since new system is basically a .NET object that
        /// implements the controller, this method saves the class in a file for examination and debugging
        /// </summary>
        /// <param name="p_sFile">File to store optimized system</param>
        /// <returns></returns>
        internal IControlEngine Generate(string p_sFile)
        {
            string sSystemCode = m_System.Generate();

            // create a writer and open the file
            TextWriter textWriter = new StreamWriter(p_sFile);

            // write the code
            textWriter.WriteLine(sSystemCode);

            // close the stream
            textWriter.Close();

            // compile the new system and return reference
            return GenerateControlSystem(sSystemCode);
        }

        /// <summary>
        /// Compiles the c# code retreived from the fuzzy system and returns a new control engine object
        /// </summary>
        /// <param name="p_sSource">Source code of the optimized system</param>
        /// <returns>Optimized system object</returns>
        private IControlEngine GenerateControlSystem(string p_sSource)
        {
            IControlEngine myobj = null;

            Microsoft.CSharp.CSharpCodeProvider cp = new Microsoft.CSharp.CSharpCodeProvider();

            System.CodeDom.Compiler.CompilerParameters cpar = new System.CodeDom.Compiler.CompilerParameters();
            cpar.GenerateInMemory = true;
            cpar.GenerateExecutable = false;

            cpar.ReferencedAssemblies.Add("system.dll");
            cpar.ReferencedAssemblies.Add("FuzzyEngine.dll");

            System.CodeDom.Compiler.CompilerResults cr = cp.CompileAssemblyFromSource(cpar, p_sSource);

            foreach (System.CodeDom.Compiler.CompilerError ce in cr.Errors)
                System.Console.WriteLine(ce.ErrorText);

            if (cr.Errors.Count == 0 && cr.CompiledAssembly != null)
            {
                Type ObjType = cr.CompiledAssembly.GetType("FuzzyOptimizedEngine");

                try
                {
                    if (ObjType != null)
                    {
                        myobj = (IControlEngine)Activator.CreateInstance(ObjType);
                    }
                }
                catch (Exception ex)
                {
                    System.Console.WriteLine(ex.Message);
                }
            }
            return myobj;
        }
    }
}
